#include <stdio.h>
#include "serial-port.h"
#include "addressmap.h"
#include "davinci.h"
#include "davinci-intc.h"

void log(char *,...);

#define N_PORTS 4
#define N_Q_SIZE 32
#define Q_SIZE_MASK 31

static union 
{
	struct NS16550 dat;
	unsigned int w[1];
	unsigned char b[1];
	unsigned short s[1];
}com[N_PORTS];

static struct  inqueue
{
char head;
char tail;
char buff[N_Q_SIZE];
} in_queue[N_PORTS];

void serial_port_handler(
	  void *lpContext,
	  int op, 
	  unsigned int dwAddress,   // start dwAddress on 32bit bus
	  char *pszBuff,           // optional 
	  unsigned int nBytes )    // in or out buffer
{
	int n_channel=*(char *)(lpContext);
	int offset;


	 if(n_channel<N_PORTS
		 &&(offset=dwAddress&0xfff)<SERIAL_PORT_ADDR_SIZE)
	 {

	
 		 if(op==ADDR_RANGE_WRITE)
		 log("UART: w %x %x,%x\n",dwAddress,offset,*(__u8 *)pszBuff);
		 switch(op)
		 {
		 case ADDR_RANGE_READ:  

			 // CTS
			 //
			 if(offset==NS16550_MSR)
			 {
				 com[n_channel].dat.mcr;
			 }
			 if(offset==NS16550_MCR)
			 {
				 com[n_channel].dat.msr;
			 }
			 if(offset==NS16550_LSR)
			 {
				 com[n_channel].dat.lsr=
				 (in_queue[n_channel].head!=in_queue[n_channel].tail)?
				 (UART_LSR_DR|0x60):(0x60);
			 }

			 if(offset==NS16550_RBR)
			 {
				 com[n_channel].dat.rbr=in_queue[n_channel].buff[in_queue[n_channel].tail++];
				 if(in_queue[n_channel].tail==N_Q_SIZE)
					 in_queue[n_channel].tail=0;
			 }
			 for(int i=0;i<nBytes;i++)
			 {
				 pszBuff[i]=com[n_channel].b[offset+i];
				 //if(pszBuff[0]<' ')
					// printf("\\0x%02x",(unsigned char)pszBuff[i]);
				 //else
					// printf("%c",pszBuff[i]);

			 }
	
			 
			 break;
		 case ADDR_RANGE_WRITE: 

			 if(offset==NS16550_RBR)
			 {
				 void serial_putc(char c);


				 serial_putc(pszBuff[0]);

				 if(pszBuff[0]<' ')
					 printf("\\0x%02x\n",(unsigned char)pszBuff[0]);
				 else
					 printf("%c",pszBuff[0]);

			 }
			 else
			 for(int i=0;i<nBytes;i++)
			 {
				 com[n_channel].b[i+offset]=pszBuff[i];

			 }
		
			 break;
		 default:
			 return;

	 }
		 if(op==1)
		 log("UART: r %x %x,%x\n",dwAddress,offset,*(__u8 *)pszBuff);
	 }

}

int enqueue(int n_ch,char c )
{

	if(in_queue[n_ch].head!=((in_queue[n_ch].tail-1)&(Q_SIZE_MASK)))
	{
		in_queue[n_ch].buff[in_queue[n_ch].head++]=c;
		if (in_queue[n_ch].head==N_Q_SIZE)
			in_queue[n_ch].head=0;

		return 0;
	}
	return 1;
}

void term_recv_char(char c)
{
	if(c!=10)
	enqueue(0,c);
	// ring bell if full

	assert_irq(IRQ_UARTINT0);

}